/**
 ******************************************************************************
 * @file	usb_core.h
 * @author	MCD Application Team
 * @version V4.0.0
 * @date	28-August-2012
 * @brief	Standard protocol processing functions prototypes
 ******************************************************************************
 */
#ifndef __USB_CORE_H
#define __USB_CORE_H

#include <stdint.h>
#include <stdbool.h>

typedef enum _CONTROL_STATE {
	WAIT_SETUP,		/* 0 */
	SETTING_UP,		/* 1 */
	IN_DATA,		/* 2 */
	OUT_DATA,		/* 3 */
	LAST_IN_DATA,	/* 4 */
	LAST_OUT_DATA,	/* 5 */
	WAIT_STATUS_IN,	/* 7 */
	WAIT_STATUS_OUT,/* 8 */
	STALLED,		/* 9 */
	PAUSE			/* 10 */
} CONTROL_STATE;	/* The state machine states of a control pipe */

typedef struct OneDescriptor {
	uint8_t *Descriptor;
	uint16_t Descriptor_Size;
} ONE_DESCRIPTOR, *PONE_DESCRIPTOR;

/* All the request process routines return a value of this type
	If the return value is not SUCCESS or NOT_READY,
	the software will STALL the correspond endpoint */
typedef enum USB_RESULT_e {
	USB_SUCCESS = 0,	/*	Process successfully */
	USB_ERROR,
	USB_UNSUPPORT,
	USB_NOT_READY		/*	The process has not been finished, endpoint will be NAK to further request */
} USB_RESULT;

/*-*-*-*-*-*-*-*-*-*-* Definitions for endpoint level -*-*-*-*-*-*-*-*-*-*-*-*/
typedef struct _ENDPOINT_INFO {
	/*
	When send data out of the device,
	CopyData() is used to get data buffer 'Length' bytes data
	if Length is 0,
		CopyData() returns the total length of the data
	if the request is not supported, returns 0
	(NEW Feature )
	if CopyData() returns -1, the calling routine should not proceed
		further and will resume the SETUP process by the class device
	if Length is not 0,
		CopyData() returns a pointer to indicate the data location
	Usb_wLength is the data remain to be sent,
	Usb_wOffset is the Offset of original data
	When receive data from the host,
	CopyData() is used to get user data buffer which is capable
	of Length bytes data to copy data from the endpoint buffer.
	if Length is 0,
		CopyData() returns the available data length,
	if Length is not 0,
		CopyData() returns user buffer address
	Usb_rLength is the data remain to be received,
	Usb_rPointer is the Offset of data buffer
	*/
	uint16_t	Usb_wLength;
	uint16_t	Usb_wOffset;
	uint16_t	PacketSize;
	uint8_t	*(*CopyData)(uint16_t Length);
} ENDPOINT_INFO;

/*-*-*-*-*-*-*-*-*-*-*-* Definitions for device level -*-*-*-*-*-*-*-*-*-*-*-*/

typedef struct _DEVICE {
	uint8_t Total_Endpoint;	 /* Number of endpoints that are used */
	uint8_t Total_Configuration;/* Number of configuration available */
} DEVICE;

typedef union {
	uint16_t w;
	struct BW {
		uint8_t bb1;
		uint8_t bb0;
	} bw;
} uint16_t_uint8_t;

typedef enum _DEVICE_STATE {
	UNCONNECTED,
	ATTACHED,
	POWERED,
	SUSPENDED,
	ADDRESSED,
	CONFIGURED
} DEVICE_STATE;

typedef struct _DEVICE_INFO {
	uint8_t	EPindex;					/*  The number of current endpoint, it will be used to specify an endpoint */
	volatile bool USB_Tx_Active;
	uint8_t USBbmRequestType;			/* bmRequestType */
	uint8_t USBbRequest;				/* bRequest */
	uint16_t_uint8_t USBwValues;		/* wValue */
	uint16_t_uint8_t USBwIndexs;		/* wIndex */
	uint16_t_uint8_t USBwLengths;		/* wLength */

	CONTROL_STATE ControlState;			/* of type CONTROL_STATE */
	uint8_t Current_Feature;
	uint8_t Current_Configuration;		/* Selected configuration */
	uint8_t Current_Interface;			/* Selected interface of current configuration */
	uint8_t Current_AlternateSetting;	/* Selected Alternate Setting of current interface*/

	volatile DEVICE_STATE bDeviceState;
	volatile bool remotewakeupon;
	uint32_t FrameCount;
	volatile uint16_t SaveRState;
	volatile uint16_t SaveTState;

	volatile uint32_t EP[8];
	ENDPOINT_INFO Ctrl_Info;

} DEVICE_INFO;

typedef struct _DEVICE_PROP {
	void (*Init)(void);		/* Initialize the device */
	void (*Reset)(void);	/* Reset routine of this device */

	/* Device dependent process after the status stage */
	void (*Process_Status_IN)(void);
	void (*Process_Status_OUT)(void);

	/* Procedure of process on setup stage of a class specified request with data stage */
	/* All class specified requests with data stage are processed in Class_Data_Setup
		Class_Data_Setup()
			responses to check all special requests and fills ENDPOINT_INFO
			according to the request
			If IN tokens are expected, then wLength & wOffset will be filled
			with the total transferring bytes and the starting position
			If OUT tokens are expected, then rLength & rOffset will be filled
			with the total expected bytes and the starting position in the buffer

			If the request is valid, Class_Data_Setup returns SUCCESS, else UNSUPPORT

		CAUTION:
		Since GET_CONFIGURATION & GET_INTERFACE are highly related to
		the individual classes, they will be checked and processed here.
	*/
	USB_RESULT (*Class_Data_Setup)(uint8_t RequestNo);

	/* Procedure of process on setup stage of a class specified request without data stage */
	/* All class specified requests without data stage are processed in Class_NoData_Setup
		Class_NoData_Setup
			responses to check all special requests and perform the request

		CAUTION:
		Since SET_CONFIGURATION & SET_INTERFACE are highly related to
		the individual classes, they will be checked and processed here.
	*/
	USB_RESULT (*Class_NoData_Setup)(uint8_t RequestNo);

	/* Class_Get_Interface_Setting
	This function is used by the file usb_core.c to test if the selected Interface
	and Alternate Setting (uint8_t Interface, uint8_t AlternateSetting) are supported by
	the application.
	This function is writing by user. It should return "SUCCESS" if the Interface
	and Alternate Setting are supported by the application or "UNSUPPORT" if they
	are not supported. */

	USB_RESULT	(*Class_Get_Interface_Setting)(uint8_t Interface, uint8_t AlternateSetting);

	uint8_t* (*GetDeviceDescriptor)(uint16_t Length);
	uint8_t* (*GetConfigDescriptor)(uint16_t Length);
	uint8_t* (*GetStringDescriptor)(uint16_t Length);

	/* This field is not used in current library version. It is kept only for
	compatibility with previous versions */
	void * RxEP_buffer;
	uint8_t MaxPacketSize;
} DEVICE_PROP;

typedef struct _USER_STANDARD_REQUESTS {
	void (*User_GetConfiguration)(void);	/* Get Configuration */
	void (*User_SetConfiguration)(void);	/* Set Configuration */
	void (*User_GetInterface)(void);		/* Get Interface */
	void (*User_SetInterface)(void);		/* Set Interface */
	void (*User_GetStatus)(void);			/* Get Status */
	void (*User_ClearFeature)(void);		/* Clear Feature */
	void (*User_SetEndPointFeature)(void);	/* Set Endpoint Feature */
	void (*User_SetDeviceFeature)(void);	/* Set Device Feature */
	void (*User_SetDeviceAddress)(void);	/* Set Device Address */
} USER_STANDARD_REQUESTS;

/* Exported constants --------------------------------------------------------*/
#define Type_Recipient	(pInformation->USBbmRequestType & (REQUEST_TYPE | RECIPIENT))

#define Usb_rLength		Usb_wLength
#define Usb_rOffset		Usb_wOffset

#define USBwValue		USBwValues.w
#define USBwValue0		USBwValues.bw.bb0
#define USBwValue1		USBwValues.bw.bb1
#define USBwIndex		USBwIndexs.w
#define USBwIndex0		USBwIndexs.bw.bb0
#define USBwIndex1		USBwIndexs.bw.bb1
#define USBwLength		USBwLengths.w
#define USBwLength0		USBwLengths.bw.bb0
#define USBwLength1		USBwLengths.bw.bb1


/* Exported functions ------------------------------------------------------- */
uint8_t Setup0_Process(void);
uint8_t Post0_Process(void);
uint8_t Out0_Process(void);
uint8_t In0_Process(void);

USB_RESULT Standard_SetEndPointFeature(void);
USB_RESULT Standard_SetDeviceFeature(void);

uint8_t * Standard_GetConfiguration(uint16_t length);
USB_RESULT Standard_SetConfiguration(void);
uint8_t * Standard_GetInterface(uint16_t length);
USB_RESULT Standard_SetInterface(void);
uint8_t * Standard_GetDescriptorData(uint16_t length, PONE_DESCRIPTOR pDesc);

uint8_t * Standard_GetStatus(uint16_t Length);
USB_RESULT Standard_ClearFeature(void);
void SetDeviceAddress(uint8_t);
void NOP_Process(void);

extern DEVICE_PROP Device_Property;
extern USER_STANDARD_REQUESTS User_Standard_Requests;
extern DEVICE Device_Table;

extern DEVICE_INFO UsbDeviceInfo;

#endif /* __USB_CORE_H */
